using System;
using System.Collections.Generic;
using NUnit.Framework;
using OpenRay;

using System.Drawing;
using System.Drawing.Imaging;
using System.Text;
using System.Security.Cryptography;
using System.Diagnostics;

namespace Tests
{
	[TestFixture]
	public class TestPinholeCamera
	{
		private void SavePNG( FrameBuffer fb, string s ) {
			Bitmap bmp = new Bitmap(fb.Width, fb.Height );
			
			for ( int y = 0; y < fb.Height; y++ )
			{
				for ( int x = 0; x < fb.Width; x++ )
				{
					float[] color = fb.GetArgb(x,y);
					
					for( int i = 0; i < 4; i++ )
					{
						color[i] = (float)Math.Max( 0, Math.Min( 1f, color[i] )  );	
					}
					
					bmp.SetPixel(x, y, System.Drawing.Color.FromArgb( 255, (int)(color[1] * 255f), (int)(color[2] * 255f), (int)(color[3] * 255f) ) );
				}
			}
			
			bmp.Save(s, ImageFormat.Png );
			bmp.Dispose();
		}
		
		private float[] CheckerBoard(float[] modelHit)
		{			
			float[] ret = new float[4];
			
			float color = ( ( (int)( modelHit[0] / 0.5 ) % 2 +
							(int)( modelHit[2] / 0.5 ) % 2 ) % 2 == 0 ) ? 0.0f : 1.0f;
			ret[0] = color;
			ret[1] = color;
			ret[2] = color;
			ret[3] = 1.0f;
			
			return ret;
		}
		
		private OpenRay.ProceduralPhongMaterial CreatePlaneMaterial()
		{
			OpenRay.Color[] ambient = new OpenRay.Color[2];
			OpenRay.IProceduralColor[] diffuse = new OpenRay.IProceduralColor[2];
			OpenRay.Color[] specular = new OpenRay.Color[2];
			OpenRay.Color[] emissive = new OpenRay.Color[2];
			OpenRay.Component[] shininess = new OpenRay.Component[2];
			
			ambient[0] = new OpenRay.Color( 0.2f, 0.2f, 0.2f, 1f );
			ambient[1] = ambient[0];
			
			diffuse[0] = new OpenRay.ProceduralColor( new ColorFunc( CheckerBoard ) );
			diffuse[1] = diffuse[0];
			
			specular[0] = new OpenRay.Color( 0.5f, 0.5f, 0.5f, 1f );
			specular[1] = specular[0];
			
			emissive[0] = new OpenRay.Color( 0f, 0f, 0f, 1f );
			emissive[1] = emissive[0];
			
			shininess[0] = new OpenRay.Component( 10f );
			shininess[1] = shininess[0];
			
			return new OpenRay.ProceduralPhongMaterial(ambient, diffuse, specular, emissive, shininess );
		}
		
		private OpenRay.PhongMaterial CreateReflectiveMaterial( OpenRay.Color d, IScene s )
		{
			OpenRay.Color[] ambient = new OpenRay.Color[2];
			OpenRay.Color[] diffuse = new OpenRay.Color[2];
			OpenRay.Color[] specular = new OpenRay.Color[2];
			OpenRay.Color[] emissive = new OpenRay.Color[2];
			OpenRay.Color[] reflective = new OpenRay.Color[2];
			float[] shininess = new float[2];
			
			ambient[0] = new OpenRay.Color( 0.2f, 0.2f, 0.2f, 1f );
			ambient[1] = ambient[0];
			
			diffuse[0] = d;
			diffuse[1] = d;
			
			specular[0] = new OpenRay.Color( 0.5f, 0.5f, 0.5f, 1f );
			specular[1] = specular[0];
			
			emissive[0] = new OpenRay.Color( 0f, 0f, 0f, 1f );
			emissive[1] = emissive[0];
			
			shininess[0] = 10f;
			shininess[1] = shininess[0];
			
			reflective[0] = new OpenRay.Color( 0.5f, 0.5f, 0.5f, 1f );
			reflective[1] = reflective[0];
			
			return new OpenRay.ReflectivePhongMaterial(ambient, diffuse, specular, emissive, shininess, reflective, s );
		}
		
		private OpenRay.PhongMaterial CreateRefractiveMaterial( OpenRay.Color d, IScene s )
		{
			OpenRay.Color[] ambient = new OpenRay.Color[2];
			OpenRay.Color[] diffuse = new OpenRay.Color[2];
			OpenRay.Color[] specular = new OpenRay.Color[2];
			OpenRay.Color[] emissive = new OpenRay.Color[2];
			OpenRay.Color[] reflective = new OpenRay.Color[2];
			float[] shininess = new float[2];
			float[] index = new float[2];
			OpenRay.Color absorbance;
			
			ambient[0] = new OpenRay.Color( 0.2f, 0.2f, 0.2f, 1f );
			ambient[1] = ambient[0];
			
			diffuse[0] = d;
			diffuse[1] = d;
			
			specular[0] = new OpenRay.Color( 0.5f, 0.5f, 0.5f, 1f );
			specular[1] = specular[0];
			
			emissive[0] = new OpenRay.Color( 0f, 0f, 0f, 1f );
			emissive[1] = emissive[0];
			
			shininess[0] = 10f;
			shininess[1] = shininess[0];
			
			reflective[0] = new OpenRay.Color( 0.0f, 0.0f, 0.0f, 1f );
			reflective[1] = reflective[0];
			
			index[0] = 1f;
			index[1] = 1.5f;
			
			absorbance = new OpenRay.Color( 0.8f, 0.8f, 1f, 1f );
			
			return new OpenRay.RefractivePhongMaterial(ambient, diffuse, specular, emissive, shininess, reflective, index, absorbance, s );
		}
		
		private List<OpenRay.Light> CreateLighting(IScene s)
		{
			List<OpenRay.Light> ret = new List<OpenRay.Light>();
			
			OpenRay.Color dim = new OpenRay.Color(0.2f, 0.2f, 0.2f, 1f );
			OpenRay.Color white = new OpenRay.Color(1f, 1f, 1f, 1f);
			OpenRay.Color black = new OpenRay.Color(0f, 0f, 0f, 1f);
			
			ret.Add( new OpenRay.DirectionalPhongLight( dim, black, black, new Vector( 1f, 0f, 0f ), null ) );
			ret.Add( new OpenRay.DirectionalPhongLight( black, white, white, new Vector( -1f, 1f, 1f ), s ) );
			
			return ret;
		}
		
		[Test]
		public void TestProceduralPlaneSphere ()
		{
			IScene s = new ArrayScene();
			
			//-----plane-----
			
			OpenRay.Point[] p = new OpenRay.Point[1] { new OpenRay.Point( 0f, -1f, 0f, 1f ) };		
			Material[] m = new Material[1] { CreatePlaneMaterial() };
			
			Geometry geom = new PlaneGeometry( p, new Vector[1] { new Vector(0f, 1f, 0f) }, m );
			Primitive prim = new Primitive( Matrix.Identity, Matrix.Identity, geom, CreateLighting(s) );
			
			s.AddPrimitive( prim );
			
			
			//------sphere------
			
			p = new OpenRay.Point[1] { new OpenRay.Point( -1f, 0f, -9f, 1f ) };	
			m = new Material[1] { CreateReflectiveMaterial(new OpenRay.Color(1f, 0f, 0f, 1f), s) };
			
			geom = new SphereGeometry(p, 1.0f, m);
			prim = new Primitive( Matrix.Identity, Matrix.Identity, geom, CreateLighting(s) );
			
			s.AddPrimitive( prim );
			
			//------sphere------
			
			p = new OpenRay.Point[1] { new OpenRay.Point( 1f, 0f, -9f, 1f ) };	
			m = new Material[1] { CreateRefractiveMaterial(new OpenRay.Color(1f, 0f, 0f, 1f), s) };
			
			geom = new SphereGeometry(p, 1.0f, m);
			prim = new Primitive( Matrix.Identity, Matrix.Identity, geom, CreateLighting(s) );
			
			s.AddPrimitive( prim );
			
			//------render------
			PinholeCamera camera = new PinholeCamera(s, 500, 500, 0.35f, 0.35f, 0.35f);
			
			SavePNG( camera.Render(), "TestPinholeCamera.png" );
		}
		
		[Test]
		public void TestProceduralPlaneSphereDof ()
		{
			IScene s = new ArrayScene();
			
			//-----plane-----
			
			OpenRay.Point[] p = new OpenRay.Point[1] { new OpenRay.Point( 0f, -1f, 0f, 1f ) };		
			Material[] m = new Material[1] { CreatePlaneMaterial() };
			
			Geometry geom = new PlaneGeometry( p, new Vector[1] { new Vector(0f, 1f, 0f) }, m );
			Primitive prim = new Primitive( Matrix.Identity, Matrix.Identity, geom, CreateLighting(s) );
			
			s.AddPrimitive( prim );
			
			
			//------sphere------
			
			p = new OpenRay.Point[1] { new OpenRay.Point( -1f, 0f, -6f, 1f ) };	
			m = new Material[1] { CreateReflectiveMaterial(new OpenRay.Color(1f, 0f, 0f, 1f), s) };
			
			geom = new SphereGeometry(p, 1.0f, m);
			prim = new Primitive( Matrix.Identity, Matrix.Identity, geom, CreateLighting(s) );
			
			s.AddPrimitive( prim );
			
			//------sphere------
			
			p = new OpenRay.Point[1] { new OpenRay.Point( 1f, 0f, -9f, 1f ) };	
			m = new Material[1] { CreateRefractiveMaterial(new OpenRay.Color(1f, 0f, 0f, 1f), s) };
			
			geom = new SphereGeometry(p, 1.0f, m);
			prim = new Primitive( Matrix.Identity, Matrix.Identity, geom, CreateLighting(s) );
			
			s.AddPrimitive( prim );
			
			//------render------
			LensCamera camera = new LensCamera(s, 500, 500, 0.35f, 0.35f, 0.35f, 0.05f, 0.33f);
			
			SavePNG( camera.Render(), "TestLensCamera.png" );
		}
	}
}

